package com.bfly.mall.order.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.bfly.common.util.DateUtil;
import com.bfly.common.util.StringUtil;
import com.bfly.core.base.service.BaseServiceImpl;
import com.bfly.core.cache.SysConfigCacheUtil;
import com.bfly.core.context.IpThreadLocal;
import com.bfly.core.enums.AccessRole;
import com.bfly.core.enums.SysError;
import com.bfly.core.exception.ServiceResponseException;
import com.bfly.mall.admin.entity.Admin;
import com.bfly.mall.advert.entity.Coupon;
import com.bfly.mall.advert.entity.CouponGetRecord;
import com.bfly.mall.advert.entity.CouponRangeBind;
import com.bfly.mall.advert.enums.CouponUseRange;
import com.bfly.mall.advert.enums.CouponUseStatus;
import com.bfly.mall.advert.service.ICouponExpensesRecordService;
import com.bfly.mall.advert.service.ICouponGetRecordService;
import com.bfly.mall.advert.service.ICouponService;
import com.bfly.mall.order.dao.OrderMapper;
import com.bfly.mall.order.dto.BookingOrderDTO;
import com.bfly.mall.order.dto.BookingOrderItemDTO;
import com.bfly.mall.order.dto.OrderStatusDTO;
import com.bfly.mall.order.entity.*;
import com.bfly.mall.order.enums.*;
import com.bfly.mall.order.event.OrderCancelEvent;
import com.bfly.mall.order.event.OrderPayEvent;
import com.bfly.mall.order.service.*;
import com.bfly.mall.product.entity.*;
import com.bfly.mall.product.enums.PricingMethodEnum;
import com.bfly.mall.product.enums.ProductActivityStatusEnum;
import com.bfly.mall.product.enums.ProductActivityTypeEnum;
import com.bfly.mall.product.enums.ShippingMethodEnum;
import com.bfly.mall.product.service.IFreightTemplateService;
import com.bfly.mall.product.service.IProductSecKillService;
import com.bfly.mall.product.service.IProductService;
import com.bfly.mall.product.service.IProductSkuService;
import com.bfly.mall.system.dto.WXMiniProConfigDTO;
import com.bfly.mall.system.dto.WXPayConfigDTO;
import com.bfly.mall.user.entity.Address;
import com.bfly.mall.user.entity.User;
import com.bfly.mall.user.service.IAddressService;
import com.bfly.mall.wechat.minipro.service.WeChatMiniProAPi;
import com.bfly.mall.wechat.minipro.service.WeChatMiniProOrderApi;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.stream.Collectors;

/**
 * @author 长沙商脉科技有限公司
 * @date 2020/10/4 16:09
 */
@Service
@Transactional(propagation = Propagation.SUPPORTS, rollbackFor = Exception.class)
public class OrderServiceImpl extends BaseServiceImpl<Order, Integer> implements IOrderService {

    private static final Logger logger = LoggerFactory.getLogger(OrderServiceImpl.class);

    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private IOrderSkuService orderSkuService;
    @Autowired
    private IOrderLogsService orderLogsService;
    @Autowired
    private IOrderAddressService orderAddressService;
    @Autowired
    private IFreightTemplateService freightTemplateService;
    @Autowired
    private IAddressService addressService;
    @Autowired
    private IProductService productService;
    @Autowired
    private IProductSkuService productSkuService;
    @Autowired
    private IOrderRefundService orderRefundService;
    @Autowired
    private ICouponGetRecordService couponUserService;
    @Autowired
    private ICouponService couponService;
    @Autowired
    private ICouponExpensesRecordService couponOrderService;
    @Autowired
    private IProductSecKillService productSecKillService;
    @Autowired
    private ApplicationContext applicationContext;
    private Lock lock = new ReentrantLock();

    @Override
    public Order getOrder(String orderNo) {
        Order order = orderMapper.getOrder(orderNo);
        return order;
    }

    @Override
    public Order getOrder(String orderNo, int userId) {
        Order order = getOrder(orderNo);
        return order == null ? null : (order.getUserId() == userId ? order : null);
    }

    /**
     * 订单数据校验
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/11/17 13:29
     */
    private void checkBookingOrder(BookingOrderDTO bookingOrder) {
        // 校验支付类型
        PayTypeEnum payTypeEnum = PayTypeEnum.getStatus(bookingOrder.getPayType());
        if (payTypeEnum == null) {
            throw new ServiceResponseException(SysError.PARAM_ERROR, "支付类型错误!");
        }
        // 校验产品等数据
        if (bookingOrder.getItems() == null || bookingOrder.getItems().isEmpty()) {
            throw new ServiceResponseException(SysError.PARAM_ERROR, "产品数据为空!");
        }
    }

    /**
     * 返回订单的地址对象
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/11/17 13:33
     */
    private Address getBookingOrderAddress(int userId, BookingOrderDTO bookingOrder) {
        ShippingMethodEnum methodEnum = ShippingMethodEnum.getShippingMethod(bookingOrder.getShippingMethod());
        Address address = null;
        if (methodEnum == ShippingMethodEnum.EXPRESS) {
            // 非自提的订单需要校验用户提交的收货地址
            address = addressService.get(bookingOrder.getAddressId());
            if (address == null || address.getUserId() != userId) {
                throw new ServiceResponseException(SysError.PARAM_ERROR, "用户地址不正确!");
            }
        }
        return address;
    }

    /**
     * 获得订单项集合
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/11/17 13:36
     */
    private List<OrderSku> getBookingOrderSku(String orderNo, BookingOrderDTO bookingOrder, int userId) {
        List<BookingOrderItemDTO> items = bookingOrder.getItems();
        List<OrderSku> orderSkus = new ArrayList<>(items.size());
        // 校验产品活动数据
        ProductActivityTypeEnum activityType = ProductActivityTypeEnum.getActivityType(bookingOrder.getActivityType());
        for (BookingOrderItemDTO item : items) {
            if (item.getBuyCount() <= 0) {
                throw new ServiceResponseException(SysError.PARAM_ERROR, "产品购买数量必须大于0!");
            }
            Product product = productService.get(item.getProductId());
            if (product == null) {
                throw new ServiceResponseException(SysError.PARAM_ERROR, "产品信息不存在!");
            }
            String productName = product.getName();
            if (!product.isOnShelf()) {
                throw new ServiceResponseException(SysError.PARAM_ERROR, productName.concat(" 已下架!"));
            }

            ProductSku productSku = productSkuService.getSku(item.getProductId(), item.getSn());
            if (productSku == null) {
                throw new ServiceResponseException(SysError.PARAM_ERROR, "产品信息不存在!");
            }

            int stock;
            BigDecimal salePrice;
            switch (activityType) {
                case SEC_KILL:
                    // 判断秒杀产品限购规格
                    ProductSecKill secKill = productSecKillService.get(bookingOrder.getActivityId());
                    if (secKill == null) {
                        throw new ServiceResponseException(SysError.PARAM_ERROR, "不存在的秒杀活动!");
                    }
                    long activityBuyCount = orderSkuService.getActivityBuyCount(userId, item.getProductId(), secKill.getId(), ProductActivityTypeEnum.SEC_KILL);
                    if (secKill.getLimits() < item.getBuyCount() || secKill.getLimits() < activityBuyCount) {
                        throw new ServiceResponseException(SysError.PARAM_ERROR, "秒杀产品限购" + secKill.getLimits() + product.getUnit());
                    }
                    // 秒杀价格
                    salePrice = secKill.getPrice();
                    break;
                default:
                    stock = productSku.getStock();
                    if (stock <= 0 || stock < item.getBuyCount()) {
                        throw new ServiceResponseException(SysError.PARAM_ERROR, productName.concat("--").concat(productSku.getTitle()).concat(" 库存不足!"));
                    }
                    // 常规价格
                    salePrice = productSku.getPrice();
                    break;
            }
            OrderSku orderSku = new OrderSku();

            BigDecimal price = salePrice.multiply(new BigDecimal(item.getBuyCount()));
            // 单种商品的总价
            orderSku.setPayPrice(price);
            orderSku.setBuyCount(item.getBuyCount());
            orderSku.setOriginalPrice(productSku.getOriginalPrice());
            orderSku.setSalePrice(salePrice);

            orderSku.setProductId(item.getProductId());
            orderSku.setOrderNo(orderNo);
            orderSku.setProductName(productName);
            orderSku.setSkuName(productSku.getTitle());
            orderSku.setCover(product.getCover());
            orderSku.setSn(productSku.getSn());
            orderSku.setUnit(product.getUnit());
            if (product.getTxt() != null) {
                orderSku.setProductRemark(product.getTxt().getRemark());
            }
            orderSkus.add(orderSku);
        }
        return orderSkus;
    }

    /**
     * 保存订单操作日志
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/12/4 18:21
     */
    private void saveOrderLogs(String orderNo, String remark, int opId, String opName, Date date, OrderOpTypeEnum typeEnum) {
        OrderLogs logs = new OrderLogs();
        logs.setCreateDate(date);
        logs.setCreateIp(IpThreadLocal.get());
        logs.setOrderNo(orderNo);
        logs.setType(typeEnum.getId());
        logs.setOperatorId(opId);
        logs.setOperatorName(opName);
        logs.setRemark(remark);
        orderLogsService.save(logs);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String bookingOrder(User user, BookingOrderDTO bookingOrder) {
        lock.lock();
        checkBookingOrder(bookingOrder);

        String orderNo = createOrderNo();
        List<OrderSku> orderSkus = getBookingOrderSku(orderNo, bookingOrder, user.getId());
        Address address = getBookingOrderAddress(user.getId(), bookingOrder);

        // 订单产品总价累加
        BigDecimal totalProductPrice = orderSkus.stream().map(OrderSku::getPayPrice).reduce(BigDecimal::add).get();

        // 订单总快递费
        BigDecimal totalExpressPrice = getOrderFreight(bookingOrder, address);

        // 订单优惠卷折扣
        BigDecimal couponDiscount = getBookingOrderCouponDiscount(user.getId(), bookingOrder, orderSkus);

        // 修改优惠卷为已使用状态 并记录优惠卷使用记录
        if (bookingOrder.getCouponId() != 0) {
            couponUserService.editStatus(bookingOrder.getCouponId(), CouponUseStatus.USED);

            CouponGetRecord couponGetRecord = couponUserService.get(bookingOrder.getCouponId());
            couponOrderService.save(couponGetRecord.getCouponId(), couponGetRecord.getCode(), orderNo);
        }

        // 保存订单收货地址
        if (bookingOrder.getShippingMethod() == ShippingMethodEnum.EXPRESS.getId()) {
            OrderAddress orderAddress = new OrderAddress();
            orderAddress.setOrderNo(orderNo);
            orderAddress.setConsignee(address.getConsignee());
            orderAddress.setPhone(address.getPhone());
            orderAddress.setProvince(address.getProvince());
            orderAddress.setProvinceId(address.getProvinceId());
            orderAddress.setCity(address.getCity());
            orderAddress.setCityId(address.getCityId());
            orderAddress.setDistrict(address.getDistrict());
            orderAddress.setDistrictId(address.getDistrictId());
            orderAddress.setAddress(address.getAddress());
            orderAddress.setPostCode(address.getPostCode());
            orderAddressService.save(orderAddress);
        }

        // 保存订单明细信息
        orderSkuService.save(orderSkus);

        // 保存订单主信息
        Order order = new Order();
        order.setActivityId(bookingOrder.getActivityId());
        order.setActivityType(bookingOrder.getActivityType());
        order.setOrderNo(orderNo);
        order.setDispatchId(bookingOrder.getShippingMethod());
        order.setTradeNo("");
        order.setRemark(bookingOrder.getRemark());
        order.setUserId(user.getId());
        order.setPayType(bookingOrder.getPayType());
        order.setExpressStatus(ExpressStatusEnum.INITIAL.getId());
        order.setPayStatus(PayStatusEnum.WAIT_PAY.getId());
        order.setOrderStatus(OrderStatusEnum.OPEN.getId());

        order.setDiscountPrice(couponDiscount);
        order.setProductPrice(totalProductPrice);
        order.setExpressPrice(totalExpressPrice);

        // 支付金额= 订单商品总金额 + 订单总运费 - 订单优惠金额
        order.setPayPrice(order.getProductPrice().add(totalExpressPrice).subtract(order.getDiscountPrice()));

        order.setCreateDate(new Date());
        order.setCreateIp(IpThreadLocal.get());
        super.save(order);

        // 更新库存和销售量
        ProductActivityTypeEnum activityType = ProductActivityTypeEnum.getActivityType(bookingOrder.getActivityType());
        orderSkus.forEach(sku -> {
            productSkuService.incrementProductStock(sku.getProductId(), sku.getSn(), -sku.getBuyCount());
            productService.incrementProductActualSales(sku.getProductId(), sku.getBuyCount());

            switch (activityType) {
                case SEC_KILL:
                    // 更新秒杀产品销售量 减去
                    ProductSecKill secKill = productSecKillService.get(bookingOrder.getActivityId());
                    if (secKill == null) {
                        throw new ServiceResponseException(SysError.PARAM_ERROR, "不存在的秒杀活动!");
                    }
                    ProductActivityStatusEnum statusEnum = ProductActivityStatusEnum.getActivityStatusEnum(secKill.getStatus());
                    if (statusEnum != ProductActivityStatusEnum.STARTED) {
                        throw new ServiceResponseException(SysError.PARAM_ERROR, String.format("秒杀活动%s", statusEnum.getName()));
                    }
                    productSecKillService.incrementProductSales(bookingOrder.getActivityId(), -sku.getBuyCount());
                    break;
            }
        });


        // 保存订单操作日志
        this.saveOrderLogs(orderNo, "提交新订单", user.getId(), user.getNickName(), order.getCreateDate(), OrderOpTypeEnum.BOOKING_MEMBER);

        logger.info(String.format("生成新订单:%s", orderNo));
        lock.unlock();
        return orderNo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public JSONObject toPayOrder(User user, String orderNo) {
        Order order = getOrder(orderNo, user.getId());
        if (order == null) {
            throw new ServiceResponseException(SysError.PARAM_ERROR, "不存在的订单信息!");
        }
        if (!order.isCanPay()) {
            String message = String.format("状态为 %s %s %s 的订单不能做支付操作!", order.getStatusName(), order.getPayStatusName(), order.getExpressStatusName());
            throw new ServiceResponseException(SysError.ERROR, message);
        }

        String prepayId = order.getPrepayId();
        // 已经提交过的订单 直接返回交易号,新订单则提交第三方支付平台下预订单 返回交易号并回写
        if (StringUtils.isBlank(prepayId)) {
            prepayId = WeChatMiniProOrderApi.payOrderToWeChat(user.getOpenId(), order);
            editOrderPrepayId(order.getOrderNo(), prepayId);
        }

        WXMiniProConfigDTO configDTO = SysConfigCacheUtil.getWxMiniProConfig();
        WXPayConfigDTO wxPayConfig = SysConfigCacheUtil.getWxPayConfig();
        String timeStamp = String.valueOf(System.currentTimeMillis());
        String nonceStr = StringUtil.getRandomString(12);
        Map<String, String> param = new LinkedHashMap<>();
        param.put("appid", configDTO.getAppId());
        param.put("timeStamp", timeStamp);
        param.put("nonceStr", nonceStr);
        param.put("package", "prepay_id=" + prepayId);
        String paySign = WeChatMiniProAPi.getWeChatPayV3Sign(param, wxPayConfig.getSerialNo(), wxPayConfig.getKeyPemPath());

        // 组装拉起微信支付组件的数据格式
        JSONObject json = new JSONObject();
        json.put("timeStamp", timeStamp);
        json.put("nonceStr", nonceStr);
        json.put("package", "prepay_id=" + prepayId);
        json.put("signType", "RSA");
        json.put("paySign", paySign);
        json.put("provider", "wxpay");
        return json;
    }

    /**
     * 计算订单优惠卷折扣
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/11/14 21:02
     */
    private BigDecimal getBookingOrderCouponDiscount(int userId, BookingOrderDTO bookingOrder, List<OrderSku> orderSkus) {
        if (bookingOrder.getCouponId() == 0) {
            return new BigDecimal(0);
        }
        CouponGetRecord couponGetRecord = couponUserService.get(bookingOrder.getCouponId());
        if (couponGetRecord == null) {
            throw new ServiceResponseException(SysError.PARAM_ERROR, "用户优惠卷不存在!");
        }
        if (couponGetRecord.getUserId() != userId) {
            throw new ServiceResponseException(SysError.PARAM_ERROR, "优惠卷【" + bookingOrder.getCouponId() + "】无效");
        }
        CouponUseStatus status = CouponUseStatus.getStatus(couponGetRecord.getStatus());
        if (status != CouponUseStatus.NOT_USE) {
            throw new ServiceResponseException(SysError.PARAM_ERROR, String.format("状态为 %s 的优惠卷不能使用!", status.getName()));
        }
        Coupon coupon = couponService.get(couponGetRecord.getCouponId());
        CouponUseRange range = CouponUseRange.getRange(coupon.getUseRange());
        if (range == CouponUseRange.ALL) {
            // 通用卷比较产品总价是否符合最小消费
            BigDecimal totalProductPrice = orderSkus.stream().map(OrderSku::getPayPrice).reduce(BigDecimal::add).get();
            if (coupon.getMinimums().compareTo(totalProductPrice) > 0) {
                throw new ServiceResponseException(SysError.PARAM_ERROR, String.format("该优惠卷为 %s 最低消费为 %s,当前订单金额不满足不能使用!", range.getName(), coupon.getMinimums()));
            }
            return coupon.getDiscount();
        }
        boolean isAllow = false;
        List<CouponRangeBind> couponRangeBinds = coupon.getCouponRangeBinds();
        List<Integer> rangeIds = couponRangeBinds.stream().map(CouponRangeBind::getRangeId).collect(Collectors.toList()); //使用范围ID集合
        BigDecimal totalPrice = new BigDecimal(0);
        for (OrderSku orderSku : orderSkus) {
            int rangeId;
            if (range == CouponUseRange.PRODUCT) {
                rangeId = orderSku.getProductId(); // 指定产品卷
            } else if (range == CouponUseRange.CATEGORY) {
                Product product = productService.get(orderSku.getProductId());
                rangeId = product.getCategoryId(); // 指定类目卷
            } else {
                throw new ServiceResponseException(SysError.PARAM_ERROR, "优惠卷使用范围未指定,当前订单不能使用!");
            }
            if (CollectionUtils.containsAny(rangeIds, Arrays.asList(rangeId))) {
                totalPrice.add(orderSku.getSalePrice().multiply(new BigDecimal(orderSku.getBuyCount())));
                isAllow = true;
            }
        }
        if (!isAllow) {
            throw new ServiceResponseException(SysError.PARAM_ERROR, String.format("该优惠卷为 '%s'类型卷,当前订单产品不满足!", range.getName()));
        }
        if (coupon.getMinimums().compareTo(totalPrice) > 0) {
            throw new ServiceResponseException(SysError.PARAM_ERROR, String.format("该优惠卷为 %s 最低消费为 %s,当前订单金额不满足不能使用!", range.getName(), coupon.getMinimums()));
        }
        return coupon.getDiscount();
    }

    @Override
    public BigDecimal calculateOrderFreight(int userId, BookingOrderDTO bookingOrder) {
        checkBookingOrder(bookingOrder);
        Address address = getBookingOrderAddress(userId, bookingOrder);
        BigDecimal freight = getOrderFreight(bookingOrder, address);
        return freight;
    }

    @Override
    public BigDecimal calculateOrderDiscount(int userId, BookingOrderDTO bookingOrder) {
        List<OrderSku> orderSkus = getBookingOrderSku(null, bookingOrder, userId);
        BigDecimal discount = getBookingOrderCouponDiscount(userId, bookingOrder, orderSkus);
        return discount;
    }

    /**
     * 计算订单的运费
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/11/14 19:45
     */
    private BigDecimal getOrderFreight(BookingOrderDTO orderDTO, Address address) {
        // 计算快递费
        BigDecimal expressPrice = new BigDecimal(0);
        if (orderDTO.getShippingMethod() == ShippingMethodEnum.SELF_MENTION.getId()) {
            // 自提运费为0
            return expressPrice;
        }
        Map<Integer, List<BookingOrderItemDTO>> templateMap = getFreightGroup(orderDTO.getItems());
        for (Integer freightId : templateMap.keySet()) {
            FreightTemplate freight = freightTemplateService.get(freightId);
            if (!freight.isFreeShipping()) {
                // 不包邮 自定义运费 系统只实现 按件数计价这种方式
                PricingMethodEnum method = PricingMethodEnum.getPricingMethod(freight.getPricingMethod());
                List<BookingOrderItemDTO> items = templateMap.get(freightId);
                switch (method) {
                    case PIECE:
                        expressPrice = calPriceByPiece(orderDTO, freight, items, address);
                        break;
                    default:
                        throw new ServiceResponseException(SysError.PARAM_ERROR, "运费计价方式配置错误!");
                }
            }
        }
        return expressPrice;
    }

    /**
     * 根据产品关联的运费模板分组，相同运费模板的统一归类
     * key为运费模板的ID，value为相同的产品
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/11/14 19:55
     */
    private Map<Integer, List<BookingOrderItemDTO>> getFreightGroup(List<BookingOrderItemDTO> items) {
        Map<Integer, List<BookingOrderItemDTO>> map = new HashMap<>();
        items.forEach(item -> {
            List<BookingOrderItemDTO> list;
            Product product = productService.get(item.getProductId());
            if (product == null || !product.isOnShelf() || product.isDeleted()) {
                throw new ServiceResponseException(SysError.PARAM_ERROR, String.format("%s 产品不存在或已被下架!", product.getName()));
            }
            if (map.containsKey(product.getFreightId())) {
                list = map.get(product.getFreightId());
            } else {
                list = new ArrayList<>();
            }
            list.add(item);
            map.put(product.getFreightId(), list);
        });
        return map;
    }

    /**
     * 根据件数计算运费价格
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/10/4 23:17
     */
    private BigDecimal calPriceByPiece(BookingOrderDTO orderDTO, FreightTemplate template, List<BookingOrderItemDTO> items, Address address) {
        BigDecimal expressPrice = new BigDecimal(0);

        List<FreightTemplatePricing> pricings = template.getPricings();
        FreightTemplatePricing productPricing = null;

        String province = address.getProvinceId() + "";
        String city = province + "-" + address.getCityId();
        int shippingMethod = orderDTO.getShippingMethod();

        // 根据地区计算费用，如果没有匹配的地区 就采用默认的全国运价计算方式 每一种模板必须有一个默认的全国计价方式
        for (FreightTemplatePricing pricing : pricings) {
            if (pricing.getShippingMethod() == ShippingMethodEnum.SELF_MENTION.getId()) {
                // 过滤掉自提的运送方式
                continue;
            }
            if (pricing.isDefaults()) {
                productPricing = pricing;
                continue;
            }
            if (pricing.getShippingMethod() != shippingMethod) {
                continue;
            }
            String citys[] = pricing.getCitys().split(",");
            if (ArrayUtils.contains(citys, city) || ArrayUtils.contains(citys, province)) {
                // 如果在指定运输地区内或省中,则按照指定地区或省的计价计算
                productPricing = pricing;
                break;
            }
        }

        // 例如: 总共10件商品 2件为首价 每增加2件 续价 1 元
        // 则首次运费2元2件，剩余8件 续件数: (8+2-1)/2=4 续费(8)= 续件数(4) * 续价(2)
        // 即 10件商品 总运费10元= 首件2元 + 续件8元
        int buyCount = 0;                               // 同种运费模板的产品总数
        BigDecimal totalPrice = new BigDecimal(0);  // 同种运费模板的产品总价
        ProductActivityTypeEnum activityType = ProductActivityTypeEnum.getActivityType(orderDTO.getActivityId());
        for (BookingOrderItemDTO item : items) {
            buyCount += item.getBuyCount();

            BigDecimal salePrice;
            switch (activityType) {
                case SEC_KILL:
                    ProductSecKill secKill = productSecKillService.get(orderDTO.getActivityId());
                    salePrice = secKill.getPrice();
                    break;
                default:
                    ProductSku productSku = productSkuService.getSku(item.getProductId(), item.getSn());
                    salePrice = productSku.getPrice();
                    break;
            }
            totalPrice = totalPrice.add(salePrice.multiply(new BigDecimal(item.getBuyCount())));
        }

        expressPrice = expressPrice.add(productPricing.getFirstPrice());       // 计算首件
        int surplus = buyCount - productPricing.getFirstPiece();              // 计算剩余件数
        if (surplus > 0) {
            int totalPiece = (surplus + productPricing.getNextPiece() - 1) / productPricing.getNextPiece();   // 计算需要续件的件数
            BigDecimal surplusPrice = productPricing.getNextPrice().multiply(new BigDecimal(totalPiece));    // 计算剩余的续件费用
            expressPrice = expressPrice.add(surplusPrice);
        }
        if (template.isFreeConditions()) {
            // 如果指定了包邮条件 判断是否符合包邮条件
            List<FreightFreeConditions> conditions = template.getConditions();
            // 先判断包邮的运送方式以及是否在包邮地区内 如果在包邮地区内
            for (FreightFreeConditions condition : conditions) {
                if (condition.getShippingMethod() != shippingMethod) {
                    continue;
                }
                String citys[] = condition.getCitys().split(",");
                PricingMethodEnum methodEnum = PricingMethodEnum.getPricingMethod(condition.getPricingMethod());

                if (ArrayUtils.contains(citys, city) || ArrayUtils.contains(citys, province)) {
                    // 满足包邮件数 则运费置为0
                    if (methodEnum == PricingMethodEnum.PIECE || methodEnum == PricingMethodEnum.PIECE_MONEY) {
                        if (buyCount > condition.getPieces()) {
                            expressPrice = expressPrice.multiply(new BigDecimal(0));
                            break;
                        }
                    }
                    // 满足指定金额包邮 则运费置为0
                    if (methodEnum == PricingMethodEnum.MONEY || methodEnum == PricingMethodEnum.PIECE_MONEY) {
                        if (totalPrice.compareTo(condition.getPrices()) >= 0) {
                            expressPrice = expressPrice.multiply(new BigDecimal(0));
                            break;
                        }
                    }
                    // 包邮条件在指定的城市 系统以匹配第一个为准
                    break;
                }
            }
        }
        return expressPrice;
    }

    /**
     * 生成订单号
     *
     * @author 长沙商脉科技有限公司
     * @date 2020/4/19 19:34
     */
    public String createOrderNo() {
        String orderNo;
        while (true) {
            orderNo = DateUtil.getCurrentDateTime().concat(StringUtil.getRandomNumber(8));
            Map<String, Object> param = new HashMap<>(1);
            param.put("order_no", orderNo);
            long count = getCount(param);
            if (count > 0) {
                continue;
            }
            break;
        }
        return orderNo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void paySuccessOrder(String orderNo, String transactionId, Date payTime, int opId, String opName, OrderOpTypeEnum typeEnum, AccessRole role) {
        Order order = orderMapper.getOrder(orderNo);
        if (order.isCanPay()) {
            // 支付成功修改支付状态为已支付，物流状态为待发货状态
            orderMapper.editPayStatus(orderNo, PayStatusEnum.SUCCESS.getId());
            orderMapper.editExpressStatus(orderNo, ExpressStatusEnum.WAIT_SHIP.getId());
            orderMapper.editOrderTransactionId(orderNo, transactionId);

            this.saveOrderLogs(orderNo, "订单支付成功!", opId, opName, payTime, typeEnum);
            logger.info(String.format("订单支付成功:[%s]", orderNo));

            // 发送支付通知
            OrderPayEvent event = new OrderPayEvent(applicationContext, order.getOrderNo(), payTime);
            applicationContext.publishEvent(event);
            return;
        }
        String message = String.format("状态为 %s %s %s 的订单不能被支付!", order.getStatusName(), order.getPayStatusName(), order.getExpressStatusName());
        throw new ServiceResponseException(SysError.PARAM_ERROR, message);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancelOrder(String orderNo, String reason, int opId, String opName, AccessRole role) {
        Order order = getOrder(orderNo);
        OrderOpTypeEnum typeEnum = null;
        if (role == AccessRole.USER) {
            if (opId != order.getUserId()) {
                throw new ServiceResponseException(SysError.RESOURCE_NOT_OWNER, "不存在的订单信息!");
            }
            typeEnum = OrderOpTypeEnum.CANCEL_ORDER_MEMBER;
        } else if (role == AccessRole.MANAGER) {
            typeEnum = OrderOpTypeEnum.CANCEL_ORDER_SHOPKEEPER;
        } else if (role == AccessRole.SYSTEM) {
            typeEnum = OrderOpTypeEnum.PAY_TIMEOUT_SYSTEM;
        }
        if (order.isCanCancel()) {
            if (order.getExpressStatus() == ExpressStatusEnum.WAIT_SHIP.getId() && order.getPayStatus() == PayStatusEnum.SUCCESS.getId()) {
                // 待发货的且支付完成的订单，取消订单操作 回退到退款流程
                orderRefundService.applyRefund(order.getOrderNo(), RefundTypeEnum.MONEY, reason, opId, opName, role);
            } else {
                // 未支付状态的订单可以取消
                orderMapper.editOrderStatus(orderNo, OrderStatusEnum.CANCEL.getId());
                if (StringUtils.isNotBlank(order.getPrepayId())) {
                    //对于已经在支付平台下了预订单的订单 同步关闭平台交易订单
                    if (order.getPayType() == PayTypeEnum.WECHAT.getId()) {
                        WeChatMiniProOrderApi.closeOrderFromWeChat(orderNo);
                    }
                }
            }

            // 更新库存和销售量
            ProductActivityTypeEnum activityType = ProductActivityTypeEnum.getActivityType(order.getActivityType());
            List<OrderSku> orderSkus = order.getSkus();
            orderSkus.forEach(sku -> {
                productSkuService.incrementProductStock(sku.getProductId(), sku.getSn(), sku.getBuyCount());
                productService.incrementProductActualSales(sku.getProductId(), -sku.getBuyCount());

                switch (activityType) {
                    case SEC_KILL:
                        // 更新秒杀产品销售量 加回
                        productSecKillService.incrementProductSales(order.getActivityId(), sku.getBuyCount());
                        break;
                }
            });

            // 记录订单日志
            this.saveOrderLogs(orderNo, reason, opId, opName, new Date(), typeEnum);
            super.getCache().clear();
            logger.info(String.format("订单取消成功:%s", orderNo));

            // 发送取消通知
            OrderCancelEvent event = new OrderCancelEvent(applicationContext, order.getOrderNo(), reason);
            applicationContext.publishEvent(event);
            return;
        }
        String message = String.format("状态为 %s %s %s 的订单不能做取消操作!", order.getStatusName(), order.getPayStatusName(), order.getExpressStatusName());
        throw new ServiceResponseException(SysError.PARAM_ERROR, message);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void confirmReceiveOrder(String orderNo, String remark, int opId, String opName, AccessRole role) {
        Order order = getOrder(orderNo);
        if (order.isCanConfirmReceive()) {
            // 记录订单日志
            OrderOpTypeEnum typeEnum = null;
            if (role == AccessRole.USER) {
                typeEnum = OrderOpTypeEnum.CONFIRM_RECEIVE_MEMBER;
                if (order.getUserId() != opId) {
                    throw new ServiceResponseException(SysError.RESOURCE_NOT_OWNER, "不存在的订单信息!");
                }
            } else if (role == AccessRole.SYSTEM) {
                typeEnum = OrderOpTypeEnum.RECEIVE_TIMEOUT_SYSTEM;
            }
            // 修改订单为已完成 已收货
            orderMapper.editExpressStatus(orderNo, ExpressStatusEnum.RECEIVED.getId());
            orderMapper.editOrderStatus(orderNo, OrderStatusEnum.COMPLETE.getId());
            this.saveOrderLogs(orderNo, remark, opId, opName, new Date(), typeEnum);
            super.getCache().clear();
            logger.info(String.format("订单确认收货:[%s]", orderNo));
            return;
        }
        String message = String.format("状态为 %s %s %s 的订单不能做确认收货操作!", order.getStatusName(), order.getPayStatusName(), order.getExpressStatusName());
        throw new ServiceResponseException(SysError.PARAM_ERROR, message);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void shopKeeperConfirmReceiveForRefund(Admin admin, String orderNo) {
        OrderRefund refund = orderRefundService.getLatestOrderRefund(orderNo);
        if (refund.getStatus() != RefundStatusEnum.PASS.getId()) {
            RefundStatusEnum statusEnum = RefundStatusEnum.getStatus(refund.getStatus());
            String message = String.format("该订单的退款申请单状态为 %s 不能做确认收货操作!", statusEnum.getName());
            throw new ServiceResponseException(SysError.ERROR, message);
        }
        Order order = getOrder(orderNo);
        if (order.isCanConfirmReceiveShopKeeper()) {
            // 商家待收货状态时，商家触发此操作是商家确认收货，商家确认收货后才能发起退款操作
            editExpressStatus(orderNo, ExpressStatusEnum.RETURN_RECEIVED);

            // 保存订单退款退货操作日志
            this.saveOrderLogs(orderNo, "", admin.getId(), admin.getUserName(), new Date(), OrderOpTypeEnum.CONFIRM_RECEIVE_SHOPKEEPER);
            logger.info(String.format("[%s]:退货订单商户确认收货", orderNo));
            return;
        }
        String message = String.format("状态为 %s %s %s 的订单不能做确认收货操作!", order.getStatusName(), order.getPayStatusName(), order.getExpressStatusName());
        throw new ServiceResponseException(SysError.ERROR, message);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void editOrderStatus(String orderNo, OrderStatusEnum status) {
        orderMapper.editOrderStatus(orderNo, status.getId());
        super.getCache().clear();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void editPayStatus(String orderNo, PayStatusEnum status) {
        orderMapper.editPayStatus(orderNo, status.getId());
        super.getCache().clear();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void editExpressStatus(String orderNo, ExpressStatusEnum status) {
        orderMapper.editExpressStatus(orderNo, status.getId());
        super.getCache().clear();
    }

    @Override
    @Transactional
    public void editOrderPrepayId(String orderNo, String prepayId) {
        orderMapper.editOrderPrepayId(orderNo, prepayId);
        super.getCache().clear();
    }

    @Override
    public OrderStatusDTO getStatuses(String orderNo) {
        return orderMapper.getStatuses(orderNo);
    }
}
